/*
 * Copyright (c) 2009, Takashi Ito
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the authors nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "sys/queue.h"

#ifndef _TSSP_H_
#define _TSSP_H_

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

#define TSSP_STATUS_OK                  (0)
#define TSSP_STATUS_FAILED              (1)
#define TSSP_STATUS_MEMORY_ERROR        (2)
#define TSSP_STATUS_TIMEOUT             (3)

#define TSSP_SECTION_FILTER_MASK_LEN    (8)      /**< Number of bytes in filter mask */
#define TSSP_UNKNOWN_PID                (0xFFFF) /**< Unknown PID */
#define TSSP_UNKNOWN_VERSION            (0xFFFF) /**< Unknown version */
#define TSSP_UNKNOWN_NETWORK_ID         (0xFFFF) /**< Unknown network_id */
#define TSSP_UNKNOWN_TS_ID              (0xFFFF) /**< Unknown transport_stream_id */
#define TSSP_UNKNOWN_SERVICE_ID         (0xFFFF) /**< Unknown service_id */
                                        
#define TSSP_PID_PAT                    (0x0000) /**< PAT packet ID */
#define TSSP_PID_CAT                    (0x0001) /**< CAT packet ID */
#define TSSP_PID_NIT                    (0x0010) /**< NIT packet ID */
#define TSSP_PID_DVB_SDT                (0x0011) /**< DVB SDT packet ID */
#define TSSP_PID_DVB_EIT                (0x0012) /**< DVB EIT packet ID */
#define TSSP_PID_BIT                    (0x0024) /**< BIT packet ID */

#define TSSP_TABLE_ID_PAT               (0x00)   /**< PAT table ID */
#define TSSP_TABLE_ID_PMT               (0x02)   /**< PMT table ID */
#define TSSP_TABLE_ID_DVB_SDT           (0x42)   /**< SDT table ID */
#define TSSP_TABLE_ID_DVB_BAT           (0x4A)   /**< BAT table ID */
#define TSSP_TABLE_ID_DVB_AIT           (0x74)   /**< AIT table ID */
#define TSSP_TABLE_ID_DVB_EIT_ACTUAL_PF (0x4E)   /**< EIT actual p/f table ID */
#define TSSP_TABLE_ID_DVB_EIT_OTHER_PF  (0x4F)   /**< EIT other p/f table ID */

#define TSSP_NUMELEM(array)             (sizeof(array)/sizeof(*array))

#define TSSP_TO_UINT12(data)            (((0x0F & (data)[0]) << 8) + (data)[1])
#define TSSP_TO_UINT16(data)            (((data)[0] << 8) + (data)[1])
#define TSSP_TO_UINT24(data)            (((data)[0] << 16) + TSSP_TO_UINT16(&(data)[1]))
#define TSSP_TO_UINT32(data)            (((data)[0] << 24) + TSSP_TO_UINT24(&(data)[1]))
#define TSSP_TO_UINT40(data)            (((uint64_t)(data)[0] << 32) + TSSP_TO_UINT32(&(data)[1]))
#define TSSP_TO_UINT48(data)            (((uint64_t)(data)[0] << 40) + TSSP_TO_UINT32(&(data)[1]))


/**
 * Private section
 */
struct tssp_section {
    uint8_t table_id;               /**<  8 table_id */
    uint8_t syntax_indicator;       /**<  1 section_syntax_indicator */
    uint8_t private_indicator;      /**<  1 private_indicator */
    uint16_t length;                /**< 12 section_length */

    struct {
        uint8_t *raw;
        uint8_t *start;             /**< private_data_byte */
        uint8_t *end;
    } data;

    struct {
        void *instance;
        void (*destroy)(struct tssp_section *);
    } impl;

    /* valid when section_syntax_indicator is not zero */
    uint16_t extension;             /**< 16 table_id_extension */
    uint8_t version;                /**<  5 version_number */
    uint8_t current_next;           /**<  1 current_next_indicator */
    uint8_t number;                 /**<  8 section_number */
    uint8_t last_number;            /**<  8 last_section_number */

    struct tssp_section *next_section;
};

/**
 * Descriptor
 */
struct tssp_descriptor {
  uint8_t tag;                      /**< 8 descriptor_tag */
  uint8_t length;                   /**< 8 descriptor_length */
  uint8_t *data;                    /**<   content */

  void *decoded;                    /**< decoded descriptor */
  void (*destroy)(void *decoded);   /**< destructor for the decoded pointer */

  STAILQ_ENTRY(tssp_descriptor) entry;
};

STAILQ_HEAD(tssp_descriptors, tssp_descriptor);

struct tssp_timer_handle;

typedef void (*tssp_timer_callback)(void *userdata);

typedef
struct tssp_timer_handle *(*tssp_timer_create)(uint32_t timeout,
                                               tssp_timer_callback callback,
                                               void *userdata);

typedef void (*tssp_timer_delete)(struct tssp_timer_handle *handle);


struct tssp_section_listener;
STAILQ_HEAD(tssp_section_listeners, tssp_section_listener);

struct tssp_section_filter {
    uint16_t pid;
    uint8_t mask[TSSP_SECTION_FILTER_MASK_LEN];
    uint8_t value[TSSP_SECTION_FILTER_MASK_LEN];
    uint32_t timeout;
};

/**
 * Callback type definition for section acquisition.
 */
typedef void (*tssp_section_callback)(int status,
                                      struct tssp_section_listener *listener,
                                      struct tssp_section *section,
                                      void *userdata);

struct tssp {
    struct tssp_section_listeners section_listeners;

    tssp_timer_create timer_create;
    tssp_timer_delete timer_delete;
    void *timer_userdata;

    struct {
        void *instance;
        struct tssp_section_listener *
            (*add_section_filter)(struct tssp *,
                                  const struct tssp_section_filter *,
                                  tssp_section_callback,
                                  void *);
        void (*remove_section_filter)(struct tssp *,
                                      struct tssp_section_listener *);
        void (*push_packet)(struct tssp *, const uint8_t *);
        void (*destroy)(struct tssp *);
    } impl;
};

void tssp_section_destroy(struct tssp_section *section);

struct tssp_descriptor *
tssp_descriptor_create(uint8_t tag, uint8_t length, const uint8_t *data);

void tssp_descriptor_destroy(struct tssp_descriptor *descriptor);

void tssp_descriptors_destroy(struct tssp_descriptors *descriptors);

/**
 * @return the number of descriptors added
 * @retval -1 fatal error occured
 */
int tssp_descriptor_parse(struct tssp_descriptors *descriptors,
                          const uint8_t *start,
                          const uint8_t *end,
                          uint16_t info_length);

/**
 * Create a TS section parser instance.
 */
struct tssp *tssp_create(tssp_timer_create timer_create_func,
                         tssp_timer_delete timer_delete_func,
                         void *timer_userdata);
void tssp_destroy(struct tssp *parser);

/**
 * Inject a packet to the section parser.
 */
void tssp_push_packet(struct tssp *parser, const uint8_t *packet);

struct tssp_section_listener *
tssp_add_section_filter(struct tssp *parser,
                        const struct tssp_section_filter *filter,
                        tssp_section_callback callback,
                        void *userdata);

void tssp_remove_section_filter(struct tssp *parser,
                                struct tssp_section_listener *listener);

void tssp_section_filter_init(struct tssp_section_filter *filter,
                              uint16_t pid,
                              uint8_t table_id,
                              uint16_t table_id_extension,
                              uint16_t table_id_extension_mask,
                              uint8_t current_next_indicator,
                              uint32_t timeout);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* _TSSP_H_ */

